/*
*    Phantom v2.0 GUI Automation Software
*    Copyright (C) 2009  John C. Van Arsdall (john@phantomtest.com)
*
*    This program is free software; you can redistribute it and/or modify
*    it under the terms of the GNU General Public License as published by
*    the Free Software Foundation; either version 2 of the License, or
*    (at your option) any later version.
*
*    This program is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
*    You should have received a copy of the GNU General Public License along
*    with this program; if not, write to the Free Software Foundation, Inc.,
*    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
// Data.cpp: implementation of the CData class.
//
//////////////////////////////////////////////////////////////////////

#include "ssi_data.h"
#include "ssi_datatypes.h"
#include "ssi_datafactory.h"
#include "ssi_variable.h"
#include "ssi_statement.h"
#include "ssi_functionset.h"
#include "ssi_functionset.h"
#include "ssi_functionapi.h"
#include "ssi_interpreter.h"

#include "ssi_data.h"
#include "ssi_data.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CData* CData::NewData(SSI_INT64 Type)
{
	return Factory_MakeData(Type);
}

CData::CData()
{
	FuncSet = SSI_NULL;

}

CData::~CData()
{

}


CData& CData::operator =(CData &Other)
{
  CopyData(&Other);
	return *this;
}

CData::operator int()
{
  if(Type == 0) return 0;
  return 1;
}

SSI_INT64 CData::GetType(void)
{
	return Type;
}

CData* CData::operator_plus(CData* InData, OpInfoT* Info)
{
	return SSI_NULL;
}

CData* CData::operator_equal(CData* InData, OpInfoT* Info)
{
	return SSI_NULL;
}

CData* CData::operator_minus(CData* InData, OpInfoT* Info)
{
	return SSI_NULL;
}

CData* CData::operator_bit_and(CData* InData, OpInfoT* Info)
{
	return SSI_NULL;
}

CData* CData::operator_bit_or(CData* InData, OpInfoT* Info)
{
	return SSI_NULL;
}

CData* CData::operator_multiply(CData* InData, OpInfoT* Info)
{
	return SSI_NULL;
}

CData* CData::operator_modulous(CData* InData, OpInfoT* Info)
{
	return SSI_NULL;
}

CData* CData::operator_divide(CData* InData, OpInfoT* Info)
{
	return SSI_NULL;
}

CData* CData::operator_power(CData* InData, OpInfoT* Info)
{
	return SSI_NULL;
}

/* Deprecated
CStatus CData::EvalClass(VarItor InVars, VarTree& InTree, CStatement& aStat)
{
	// Operator not defined for this
	return STAT_ERR;
}
*/

SSI_BOOL CData::DeleteData(CData* In)
{
	return 0;
}

SSI_BOOL CData::boolean()
{
  return 0;
}

CStatus CData::InitData(void)
{

	// This function is over-ridden
	// Need to decide how functions will be handled in 
	//  copying data / creating new data
	// Do we:
	//  1.  Call InitFunctions whenever data is created
	//  2.  Copy the FuncSet whenever data is copied, else
	//      call InitFunctions
	//  3.  Have a global set that is only set once, make
	//      a singleton that returns a FuncSet memeber if
	//      calling data has existing Type

	return STAT_OK;
}

CData* CData::MakeData_Internal(void)
{

	// Make sure the FuncSet, etc... is deleted, or what the heck is supposed to happen
	// Actually, never mind
	CData* Ret = MakeData();
	Ret->FuncSet = FuncSet;
	Ret->VarSet = VarSet;

	return Ret;
}

CStatus CData::AddFunction(CFunctionDecl& NewFunc)
{
	if(!FuncSet){
		return STAT_ERR;  // This is an internal error
	}
	return FuncSet->AddFunction(NewFunc);
}

CStatus CData::EvalFunction(VarItor TopVar, VarTree& InTree, CStatement& aStat)
{
	if(!FuncSet){
		return STAT_ERR;  // This is an internal error
	}

	// If TopVar... Find Var from registered list, send to EvalFunction
	CStatus RetStat;
  CFunctionDecl aDecl;
	SSI_FunctionAPI theAPI;

	theAPI.m_Engine = aStat.Engine;

	RetStat = FuncSet->CheckFunction(aStat.aFunction, theAPI, aDecl);
	if(!RetStat) return RetStat;

	if(aDecl.FunctionType == CDATA_FUNCTION_TYPE){
		this->SSI_FunctionProcessor(theAPI);
	}else{
		return STAT_ERR;  // This is an internal error
	}
  
  if(!theAPI.m_ReturnStatus){
    return theAPI.m_ReturnStatus;
  }
  
	CVariable* pVar = theAPI.GetReturnData();
	if(!pVar){
		if(!theAPI.m_ReturnStatus){
			return theAPI.m_ReturnStatus;
		}
		MAKEERR2(ERR03335, aDecl.Name.Str(), CInterpreter::GetVariableString(aDecl.ReturnType).Str());
	}

  if(theAPI.GetReturnData()->GetType() != aDecl.ReturnType){
    MAKEERR2(ERR03336, aDecl.Name.Str(), CInterpreter::GetVariableString(aDecl.ReturnType).Str());
  }

	//FuncRetVar = *pVar;
	aStat.aFunction.SetReturn(*pVar);
	return theAPI.m_ReturnStatus;
}

void CData::SSI_FunctionProcessor(SSI_FunctionAPI& theAPI)
{
	// Virtual Function
}

CStatus CData::AddVariable(CVariable& newVar)
{
	String RetMess;
	CStatus RetStat;

  VarItor Itor = VarSet->begin();
  while(Itor != VarSet->end()){
    if((*Itor).Name == newVar.Name){
      MAKEERR1(ERR02025, newVar.Name.Str());
    }
    Itor++;
  }


  /*
  CANT USE AS WILL ALLOW MULTIPLE VARIABLE NAMES OF DIFFERENT TYPES
  VarItor Itor = find(VarSet->begin(), VarSet->end(), newVar);  //VarSet->find(newVar);
  if(Itor != VarSet->end())
  {
    RetMess.Format("Variable '%s' already exists", newVar.Name.Str());
    RetStat.Set(sERROR, RetMess);
    return RetStat;
  }
  */
	VarSet->push_back(newVar);
  //aNode = VarSet->push_back(newVar);
  //if(!aNode)
  //{
  //  // Come up with some other error
  //  return CStatus(sERROR, ERR02001);;
  //}
  return STAT_SUCCESS;
}

SSI_BOOL CData::GetVariable(CVariable& Var, CInterpreter* anInterp)
{
  VarItor Itor;/// = SSI_NULL;
	DimNode dNode = SSI_NULL;
	String Name = Var.Name;
	String Temp;
	  
	if(!dNode){
		if(dNode = anInterp->GetArrayDimensions(Name, Temp)){
			Var.Name = Temp;
		}
	}

	if(dNode == ERROR_NODE){
		return 0;
	}

	Itor = find(VarSet->begin(), VarSet->end(), Var);
	//aNode = VarSet->find(Var);

	if(Itor != VarSet->end())
  {
		Var = *Itor;
    //aNode->Get(Var);
		if(dNode){
			Var.SetWorkingNode(dNode); // OK, because dNode newly created
		}else{
			Var.SetWorkingNode(SSI_NULL);
		}
		if(!SSI_UpdateVariable(Var, 1)) return 0;
		//aNode->Set(Var);
		*Itor = Var;
    return 1;
	}else{
		// In a case where local variables are found...
    // I dont think we want this anymore, since this
    // is a 'Get'.  The array index is handled elsewhere
    // so the UpdateVariable call should just return the entire
    // variable.  If this is included, and dNode is destroyed,
    // will cause crash.
		//if(dNode){
		//	Var.SetWorkingNode(dNode);  // OK, because dNode newly created
		//}else{
		//	Var.SetWorkingNode(SSI_NULL);
		//}
		if(!SSI_UpdateVariable(Var, 1)) return 0;

		// In case working node is overwritten
		if(dNode){
			Var.SetWorkingNode(dNode); // OK, because dNode newly created
		}else{
			Var.SetWorkingNode(SSI_NULL);
		}

		return 1;
	}

	//Var.Name = Name;
	return 0;
}

CStatus CData::SetVariable(CVariable& aVar)
{
  VarItor Itor;// = SSI_NULL;
  CStatus Ret;
  CVariable Temp = aVar;
	if(!GetVariable(Temp)){
		MAKEERR1(ERR02013, aVar.Name.Str());
	}
	Itor = find(VarSet->begin(), VarSet->end(), Temp);
  //aNode = VarSet->find(Temp);

	if(Itor == VarSet->end()){
		MAKEERR1(ERR02013, aVar.Name.Str());
		//if(CallingClass){
		//	// *** NEED TO ORDER THIS PROPERLY!!!!
		//	if(CallingClass->SetVariable(aVar, this, ACCESS_PRIVATE)){
		//		return STAT_SUCCESS;
		//	}
		//}
    //if(parEngine){
    //  return parEngine->SetVariable(aVar);
    //}
  }

	*Itor = aVar;
  //aNode->Set(aVar);
	return SSI_UpdateVariable(aVar, 0);
}

CStatus CData::SSI_UpdateVariable(CVariable& aVar, SSI_BOOL bGet)
{
	// By default, it should fail, unless over-ridden
	return STAT_FAIL;
}

CData* CData::operator_equal_internal(CData* InData, OpInfoT* Info)
{
	CData* Ret = operator_equal(InData, Info);
	if(!Ret) return SSI_NULL;
	Ret->VarSet = InData->VarSet;
	return Ret;
}

CStatus CData::AddVariable(CData* InData, String& Name)
{
	if(!InData) return STAT_ERR;  // This is an internal error
	
	CVariable aVar;
	aVar.Name = Name;
	aVar.SetType(InData->Type);
	aVar.SetData(InData);
	return AddVariable(aVar);

}

CStatus CData::negate(void)
{
	MAKEERR2(ERR02021, "negate", DataName.Str());
}

SSI_BOOL CData::IsFunction(const String& fName)
{
	if(!FuncSet) return 0;

	CFunctionDecl aDecl;
  aDecl.Name = fName;
  aDecl.Name.Trim();

	return FuncSet->IsFunction(aDecl);//>FindFunctionDecl(aDecl);
}

CStatus CData::operator_compare(CData* InData, SSI_INT64& Result)
{
	MAKEERR2(ERR02022, InData->DataName.Str(), DataName.Str());
}

CStatus CData::operator_increment(void)
{
	MAKEERR1(ERR02023, DataName.Str());
}

CStatus CData::operator_decrement(void)
{
	MAKEERR1(ERR02024, DataName.Str());
}

String CData::GetMemberFunctions(const String& Delim)
{
  String Ret;
	// --- MEMBER FUNCTIONS ---
	if(FuncSet){
		CFunctionDecl aDecl;
		SSI_INT64 NumParams = 0;
		SSI_INT64 ctr = 0;
		SSI_INT64 Type = 0;
		String sParams;
		String sReturn;

		FuncNode* aNode = FuncSet->Funcs.getTop();
		
		while(aNode){
			sParams = "";
			aNode->Get(aDecl);
			NumParams = aDecl.getNumParams();
			for(ctr = 0; ctr < NumParams; ctr++){
				Type = aDecl.getParamType(ctr);
				CData* aData = Factory_MakeData(Type);
				if(aData){
					sParams = sParams + aData->DataName + ",";
					delete aData;
				}
			}
			sParams.chop();

			CData* aData = Factory_MakeData(aDecl.ReturnType);
			if(aData){
				sReturn = aData->DataName;
				delete aData;
			}

      Ret = Ret + sReturn + " " + aDecl.Name + "(" + sParams + ")";
      if(aNode->next()){
        Ret = Ret + Delim;
      }
			aNode = aNode->next();
		}
	}
  return Ret;
}


void CData::DisplayMembers(ssi_stream* out)
{
	if(!out) return;
	String s;
	SSI_CHAR StrBuff[68];

	// --- MEMBER VARIABLES ---
	if(VarSet){
		CVariable Var;
		VarItor Itor = VarSet->begin();
		s = "<Member Variables>";

		CSSI_CHAR* fmt = "%-25.24s%-42.42s";

		sprintf(StrBuff, fmt, "Name", "Type");
		out->Disp(s.Str(), 1);
		out->Disp(StrBuff, 1);
		out->Disp("------------------------------------------------------------------------------", 1);

		while(Itor != VarSet->end()){
			Var = *Itor;
			//aNode->Get(Var);
			sprintf(StrBuff, fmt, Var.Name.Str(), Var.GetTypeString().Str());
			out->Disp(StrBuff, 1);
			//aNode = aNode->next();
			++Itor;
		}
	}

	// --- MEMBER FUNCTIONS ---
	if(FuncSet){
		CFunctionDecl aDecl;
		SSI_INT64 NumParams = 0;
		SSI_INT64 ctr = 0;
		SSI_INT64 Type = 0;
		String sParams;
		String sReturn;

		FuncNode* aNode = FuncSet->Funcs.getTop();
		CSSI_CHAR* fmt = "%-25.24s%-12.11s%-30.30s";
		s = "<Member Functions>";
		sprintf(StrBuff, fmt, "Name", "Return", "Parameters");
		out->Disp("", 1);
		out->Disp(s.Str(), 1);
		out->Disp(StrBuff, 1);
		out->Disp("------------------------------------------------------------------------------", 1);
		while(aNode){
			sParams = "";
			aNode->Get(aDecl);
			NumParams = aDecl.getNumParams();
			for(ctr = 0; ctr < NumParams; ctr++){
				Type = aDecl.getParamType(ctr);
				CData* aData = Factory_MakeData(Type);
				if(aData){
					sParams = sParams + aData->DataName + ",";
					delete aData;
				}
			}
			sParams.chop();

			CData* aData = Factory_MakeData(aDecl.ReturnType);
			if(aData){
				sReturn = aData->DataName;
				delete aData;
			}
			sprintf(StrBuff, fmt, (CSSI_CHAR*)aDecl.Name, (CSSI_CHAR*)sReturn, (CSSI_CHAR*)sParams);
			out->Disp(StrBuff, 1);
			aNode = aNode->next();
		}
	}
}

void CData::DisplayMembersHELP(String& FileName)
{
	String s;

	FILE* fh = fopen(FileName.Str(), "w");
	if(!fh){
		return;
	}

	fprintf(fh, "<HTML><HEAD><TITLE>Phantom v2.0 Beta - %s Member List</TITLE></HEAD>\n<BODY>\n<br><h1>Phantom v2.0 - %s Member List</h1><p>\n", DataName.Str(), DataName.Str());
	fprintf(fh, "<hr>\n");

	// --- MEMBER VARIABLES ---
	if(VarSet){
		CVariable Var;
		//VarNode* aNode = VarSet->getTop();
		VarItor Itor = VarSet->begin();
		s = "<h2>Member Variables</h2><p>\n";
		fprintf(fh, "%s", s.Str());

		while(Itor != VarSet->end()){
			//aNode->Get(Var);
			Var = *Itor;
			fprintf(fh, "<b>Name:</b> %s<br>\n<b>Type:</b> %s<br>\n<b>Description:</b> Coming Soon...<p><hr><p>\n", Var.Name.Str(), Var.GetTypeString().Str());
			++Itor;
			//aNode = aNode->next();
		}
	}

	// --- MEMBER FUNCTIONS ---
	if(FuncSet){
		CFunctionDecl aDecl;
		SSI_INT64 NumParams = 0;
		SSI_INT64 ctr = 0;
		SSI_INT64 Type = 0;
		String sParams;
		String sReturn;

		FuncNode* aNode = FuncSet->Funcs.getTop();
		s = "<h2>Member Functions</h2><p>\n";
		fprintf(fh, "%s", s.Str());

		while(aNode){
			aNode->Get(aDecl);
			NumParams = aDecl.getNumParams();
			for(ctr = 0; ctr < NumParams; ctr++){
				Type = aDecl.getParamType(ctr);
			
				CData* aData = Factory_MakeData(Type);
				if(aData){
					sParams = sParams + aData->DataName + ",";
					delete aData;
				}
			}
			sParams.chop();

			CData* aData = Factory_MakeData(aDecl.ReturnType);
			if(aData){
				sReturn = aData->DataName;
				delete aData;
			}
			String sTemp1 = sReturn;
			if(sReturn.GetLength() <= 0){
				sTemp1 = "none";
			}
			String sTemp2 = sParams;
			if(sParams.GetLength() <= 0){
				sTemp2 = "none";
			}

			fprintf(fh, "<h2><b>%s</b></h2><p>\n", aDecl.Name.Str());
			fprintf(fh, "<b>Syntax:</b><br>\n<i>%s %s.%s(%s)</i><p>\n", sReturn.Str(), DataName.Str(), aDecl.Name.Str(), sParams.Str());
			fprintf(fh, "<b>Return Type:</b> %s<br>\n", sTemp1.Str());
			fprintf(fh, "<b>Parameters:</b> %s<br>\n", sTemp2.Str());
			fprintf(fh, "<b>Description:</b> Coming soon...<p>\n");
			fprintf(fh, "<hr>\n");
		
			sParams = "";
			aNode = aNode->next();
		}
	}
	fclose(fh);
}

CStatus CData::AddLocalVariable(CData* In, String& Name){
	return STAT_ERR;
}


